package wasnake.actors;

import java.awt.Color;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Set;

import wasnake.grid.*;

/**
 * A snake actor that grows when it it runs into food and dies if it 
 * hits any other actor including itself.
 * @author Mark
 */
public class Snake extends Actor {
    /** The cells composing this snake.*/
    private LinkedList<Cell> cells;

    /**
     * Creates a new snake.
     * @param color The color of this snake.
     * @param dir The direction this snake is facing.
     */
    public Snake(Color color, Direction dir){
        super(color, dir);
        cells = new LinkedList<Cell>();
    }
    
    @Override
    public void putSelfInGrid(Grid<Cell> grid, Location location) {
        if(this.grid != null)
            throw new IllegalStateException(
                    "This snake is already contained in a grid.");
        if(!grid.isValid(location))
            throw new IllegalStateException(
                    "The location given is not valid.");
        this.grid = grid;
        // If the snake is not new, reset it
    	if(!cells.isEmpty())
            cells.clear();
    	// If the cell to place the actor in is currently occupied, remove it
        if(grid.get(location) != null)
            grid.get(location).getOwner().removeSelfFromGrid();
        // Create the snakes new head and add it to the grid
        cells.add(new Cell(this, location));
        grid.put(location, cells.getFirst());
    }

    @Override
    public void removeSelfFromGrid() {
        // Remove the snake's cells from the grid
    	Iterator<Cell> itr = cells.iterator();
    	while(itr.hasNext()){
    		Cell next = itr.next();
    		grid.remove(next.getLocation());
    	}
        grid = null;
    }

    @Override
    public Set<Cell> getCells() {
    	return new HashSet<Cell>(cells);
    }

    @Override
    public void act() {
        // Get the location of the cell to move to
        Location moveTo = cells.getFirst().getLocation()
                .getAdjacentLocation(direction);
        // If the snake runs out of bounds, it dies
    	if(!grid.isValid(moveTo)){
            removeSelfFromGrid();
            return;
        }
        // Get the cell to move the snake to
    	Cell toMoveTo = grid.get(moveTo);
        // Else if the cell to move to is empty, move the snake to the cell
    	if(toMoveTo == null) {
            // Add a new cell to the front
            Cell newHead = new Cell(this, moveTo);
            grid.put(moveTo, newHead);
            cells.addFirst(newHead);
            // Remove the last cell
            Cell oldTail = cells.getLast();
            grid.remove(oldTail.getLocation());
            cells.removeLast();
        // Else if the next cell is occupied, 
        }else {
            // and is a food, eat it and grow the snake
            if(toMoveTo.getOwner().getClass() == Food.class) {
                toMoveTo.getOwner().removeSelfFromGrid();
                Cell newHead = new Cell(this, moveTo);
                grid.put(moveTo, newHead);
                cells.addFirst(newHead);
            // and is not food, the snake dies
            }else {
                removeSelfFromGrid();
                this.grid = null;
            }
        }
    }
    
}
